package com.douqu.game.main;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bean.core.pay.alipay.AlipayConfig;
import com.bean.core.pay.tenpay.bean.TenpayConfig;
import com.bean.core.util.HttpJsonUtils;
import com.bean.core.util.MD5Utils;
import com.bean.core.util.TimeUtils;
import com.bean.core.util.Utils;
import com.douqu.game.core.controller.PlayerController;
import com.douqu.game.core.database.model.IllegalityModel;
import com.douqu.game.core.database.model.LoginLogModel;
import com.douqu.game.core.database.model.LogoutLogModel;
import com.douqu.game.core.e.E_ConnectionDestroyType;
import com.douqu.game.core.e.E_OffLineType;
import com.douqu.game.core.e.ReturnMessage;
import com.douqu.game.core.entity.Player;
import com.douqu.game.core.entity.common.TwoTuple;
import com.douqu.game.core.entity.world.*;
import com.douqu.game.core.entity.world.map.CityBean;
import com.douqu.game.core.factory.*;
import com.douqu.game.core.i.IServer;
import com.douqu.game.core.netty.NettyConnection;
import com.douqu.game.core.protobuf.SGCommonProto;
import com.douqu.game.core.util.CoreUtils;
import com.douqu.game.core.util.LogUtils;
import com.douqu.game.core.util.StringUtils;
import com.douqu.game.main.gui.MainFrame;
import com.douqu.game.main.msg.GMChannel;
import com.douqu.game.main.msg.GMSystemChannel;
import com.douqu.game.main.server.*;
import com.douqu.game.main.server.netty.NettyGMServer;
import com.douqu.game.main.server.netty.NettyTCPServer;
import com.douqu.game.main.server.rmi.RMIServer;
import com.douqu.game.main.service.IllegalityService;
import com.douqu.game.main.service.PlayerService;
import com.douqu.game.main.service.WorldService;

import javax.swing.*;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Author : Bean
 * 2017-07-06 15:43
 */
public class GameServer {


    private MainFrame mainFrame;

    private IServer tcpServer;

    private IServer rmiServer;

    private IServer gmServer;

    /**
     * 服务器外网IP
     */
    private String serverHost;

    private String version;

    private int mailLevel;

    private boolean isRunning;

    /**
     * 是否能让玩家进入游戏(不限制白名单)
     */
    private boolean isOpen;

    public static long startTime;

    /**
     * 活动大厅公共信息
     */
    public volatile long weekEndTime;
    public volatile List<SGCommonProto.WeekCalendarColumn.Builder> activityWeekInfoList = new ArrayList<>();
    public volatile long dayEndTime;
    public volatile List<SGCommonProto.ActivityHallColumn.Builder> activityDayInfoList = new CopyOnWriteArrayList<>();

    /**
     * 所有线程任务
     */
    private ExecutorService service = Executors.newCachedThreadPool();

    private WorldManager worldManager;

    private static GameServer instance = null;

    private GameServer()
    {
        worldManager = new WorldManager();
    }

    public static GameServer getInstance()
    {
        if(instance == null)
            instance = new GameServer();

        return instance;
    }

    public WorldManager getWorldManager()
    {
        return worldManager;
    }

    /**
     * 启动服务器
     */
    public void start()
    {
        DataFactory.currentTime = System.currentTimeMillis();

        SpringServer.start();

        DataFactory.currentTime = System.currentTimeMillis();

        LoadManager.ServerStartLoad();

        boolean isDev = SpringContext.isDev();
        if(!isDev)
           GMSystemChannel.createRobot(null, 2000, 0);

        //加载服务器数据
        LogUtils.info("加载WorldInfo开始----------------");
        worldManager.load();
        UpdateManager.getInstance().load();
        LogUtils.info("加载WorldInfo结束----------------");

        if(StringUtils.isNullOrEmpty(WorldInfoFactory.getInstance().getAuthServerHost()))
        {
            LogUtils.error("Error -> 认证服务器地址没有配置,请GM人员稍候登录GM后台配置");
            JOptionPane.showMessageDialog(null, "认证服务器地址没有配置,请GM人员稍候登录GM后台配置");
            System.exit(0);
            return;
        }

        if(!getServerInfo())
        {
            LogUtils.error("服务器启动失败-----------");
            System.exit(0);
            return;
        }

        int serverId = getServerId();
        String temp = ConfigFactory.LOG_PATH;
        if(temp.endsWith("/"))
        {
            temp = temp.substring(0, temp.length()-1);
        }
        String configServerId = temp.substring(temp.lastIndexOf("/") + 1, temp.length());
        int tempServerId = 0;
        try{
            tempServerId = Integer.parseInt(configServerId);
            if(tempServerId != serverId)
            {
                ConfigFactory.LOG_PATH = ConfigFactory.LOG_PATH.replace(tempServerId+"", serverId+"");
                ConfigFactory.LOG_PATH = ConfigFactory.LOG_PATH.replaceAll("//", "/");
                LogUtils.info("Fix LogPath ---------------> " + ConfigFactory.LOG_PATH);
            }
        }catch (Exception e){
            ConfigFactory.LOG_PATH = ConfigFactory.LOG_PATH + "/" + serverId;
            ConfigFactory.LOG_PATH = ConfigFactory.LOG_PATH.replaceAll("//", "/");
            LogUtils.info("Fix LogPath ---------------> " + ConfigFactory.LOG_PATH);
        }

        isRunning = true;
        startTime = System.currentTimeMillis();

        if(tcpServer == null)
        {
            tcpServer = new NettyTCPServer();
            tcpServer.start();
        }

        if(rmiServer == null)
        {
            rmiServer = new RMIServer();
            rmiServer.start();
        }

        if(gmServer == null)
        {
            gmServer = new NettyGMServer();
            gmServer.start();
        }

//        notifyServerStatus(null);

        notifyActivityServer();

        boolean isNWTest = CoreUtils.getInternetIp().equals("192.168.2.249");
        if(isDev && !isNWTest)
        {
            ConfigFactory.gmSetting.setMainLog(LogUtils.LOG_LEVEL.DEBUG, LogUtils.LOG_LEVEL.DEBUG);
            ConfigFactory.gmSetting.setActivityLog(LogUtils.LOG_LEVEL.DEBUG, LogUtils.LOG_LEVEL.DEBUG);
        }
        else if(isNWTest)
        {
            ConfigFactory.gmSetting.setMainLog(LogUtils.LOG_LEVEL.INFO, LogUtils.LOG_LEVEL.WARN);
            ConfigFactory.gmSetting.setActivityLog(LogUtils.LOG_LEVEL.INFO, LogUtils.LOG_LEVEL.WARN);
        }
        else
        {
            ConfigFactory.gmSetting.setMainLog(LogUtils.LOG_LEVEL.WARN, LogUtils.LOG_LEVEL.WARN);
            ConfigFactory.gmSetting.setActivityLog(LogUtils.LOG_LEVEL.INFO, LogUtils.LOG_LEVEL.WARN);
        }

        mainFrame.initJob();
    }

    public void stopGMServer()
    {
        if(gmServer != null)
        {
            gmServer.stop();
            gmServer = null;

            try {
                Class.forName(GMChannel.class.getName());
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    public void startGMServer()
    {
        //不从这里启动了
//        if(gmServer == null)
//        {
//            gmServer = new NettyGMServer();
//            gmServer.start();
//        }
    }

    /**
     * 停止服务器
     */
    public void stop()
    {
        try{
            service.shutdown();

            //保存所有在线玩家数据
            LogUtils.info("关闭主服务器时，保存服务器和所有在线玩家数据开始<---------->");
            WorldService worldService = SpringContext.getBean(WorldService.class);
            worldService.update();

            PlayerService playerService = SpringContext.getBean(PlayerService.class);
            playerService.saveOnlinePlayer(true);

            Map<Integer, CityBean> cityMap = WorldInfoFactory.getInstance().getWorldMapData().getCityMap();
            for(CityBean cityBean : cityMap.values())
            {
                cityBean.stopServerCheckOccupy();
            }

            RecordManager.getInstance().save();

            List<NettyConnection> connections = worldManager.getConnections();
            for(NettyConnection connection : connections)
            {
                connection.sendWarn("服务器连接断开");
                connection.setDestroyType(E_ConnectionDestroyType.SERVER_CLOSE);
                connection.sendOffline();
            }
            List<PlayerController> playerList = worldManager.getPlayerList();
            for(PlayerController playerController : playerList)
            {
                ServerManager.getInstance().sendOffline(playerController);
            }

            LogUtils.info("关闭主服务器时，保存服务器和所有在线玩家数据成功<---------->");

            WorldInfoFactory.getInstance().setServerStatus(SGCommonProto.E_SERVER_STATUS.SERVER_STATUS_CLOSE);

//            notifyServerStatus(null);

            TimeUtils.sleep(1000);

            if(tcpServer != null)
            {
                tcpServer.stop();
            }

            if(rmiServer != null)
            {
                rmiServer.stop();
            }

            if(gmServer != null)
            {
                gmServer.stop();
            }

            SpringContext.stop();

            isRunning = false;

            System.exit(0);
        }catch (Exception e){
            e.printStackTrace();
            LogUtils.errorException(e);
        }
    }



    public void startThread(Runnable runnable)
    {
        service.execute(runnable);
    }


    public int getServerId()
    {
        return WorldInfoFactory.getInstance().getServerId();
    }

    public void notifyUpdateVersion(String version)
    {
        setVersion(version);

        StringBuilder sb = new StringBuilder();
        sb.append(GameServer.getInstance().getAuthServerHost());
        sb.append(HttpFactory.SERVER_UPDATE_VERSION);
        sb.append("?serverId=");
        sb.append(WorldInfoFactory.getInstance().getServerId());
        sb.append("&version=");
        sb.append(version);
        sb.append("&password=");
        sb.append(CoreUtils.MD5Encode(String.valueOf(WorldInfoFactory.getInstance().getServerId())));

        HttpJsonUtils.httpPost(sb.toString());
    }

    public boolean getServerInfo()
    {
        int serverId = WorldInfoFactory.getInstance().getServerId();

        String url = getAuthServerHost() + HttpFactory.SERVER_GET_SERVER_INFO + "?id=" + serverId;
        JSONObject serverInfoJson = HttpJsonUtils.httpGet(url);
        if(serverInfoJson == null || !serverInfoJson.containsKey("code") || !serverInfoJson.getString("code").equals(ReturnMessage.SUCCESS.getCode()))
        {
            LogUtils.error("从认证服务器获取信息错误，请检查 -> " + url);
            JOptionPane.showMessageDialog(null, "从认证服务器获取信息错误，请检查 -> " + url);
            return false;
        }

        url = getAuthServerHost() + HttpFactory.SERVER_GET_SERVER_TASK + "?id=" + serverId;
        JSONObject serverTaskJson = HttpJsonUtils.httpGet(url);
        if(serverTaskJson == null || !serverTaskJson.containsKey("code") || !serverTaskJson.getString("code").equals(ReturnMessage.SUCCESS.getCode()))
        {
            LogUtils.error("从认证服务器获取计划任务错误，请检查 -> " + url);
            JOptionPane.showMessageDialog(null, "从认证服务器获取计划任务错误，请检查 -> " + url);
            return false;
        }

        boolean getConfig = getPlatformConfigFromAuth("");
        if(!getConfig)
        {
            JOptionPane.showMessageDialog(null, "从认证服获取冲值配置错误,请检查 -> " + getAuthServerHost());
            return false;
        }

        LogUtils.info("Load Tenpay Start============================>");
        LogUtils.info("\n" + TenpayConfig.showInfo());
        LogUtils.info("Load Tenpay Success==========================>");

        LogUtils.info("Load Alipay Start============================>");
        LogUtils.info("\n" + AlipayConfig.showInfo());
        LogUtils.info("Load Alipay Success==========================>");

        LogUtils.info("Load Platform Start============================>");
        PlatformFactory.showInfo(false);
        LogUtils.info("Load Platform Success==========================>");

        JSONObject serverInfo = serverInfoJson.getJSONObject("data");

        LogUtils.info("从认证服务器获取到当前服务器数据 ------> " + serverInfo);
        version = serverInfo.getString("version");
        mailLevel = serverInfo.getInteger("mailLevel");
        GameServer.getInstance().setServerHost(serverInfo.getString("ip"));
        long firstOpenTime = serverInfo.getLong("firstOpenTime");
        WorldInfoFactory.getInstance().setStartServerTime(new Date(firstOpenTime));
        String serverName = serverInfo.getString("remark");
        if(!Utils.isNullOrEmpty(serverName))
            WorldInfoFactory.getInstance().setName(serverName);

        LogUtils.info("服务器IP -> " + GameServer.getInstance().getServerHost() + " 版本 -> " + version);

        JSONArray taskList = serverTaskJson.getJSONArray("data");
        LogUtils.info("从认证服务器获取计划任务 -> " + taskList);
        UpdateManager.getInstance().initServerTask(taskList);

        LogUtils.info("当前服务器计划任务列表 -> " + UpdateManager.getInstance().getServerTaskList());

        //从认证服拉取白名单列表
        GameServer.getInstance().getWorldManager().updateWhite();
        LogUtils.info("获取白名单列表成功！");
        return !CoreUtils.isNullOrEmpty(GameServer.getInstance().getServerHost());
    }

    public boolean notifyActivityServer()
    {
        String url = GameServer.getInstance().getAuthServerHost() + HttpFactory.SERVER_NOTIFY_ACTIVITY_SERVER_MAIN_SERVER_START + "?serverId=" + WorldInfoFactory.getInstance().getServerId();

        LogUtils.warn("通知活动服务器我开启了 -> " + url);

        JSONObject result = HttpJsonUtils.httpPost(url);
        if(result == null)
            return false;

        if(!result.getString("code").equals(ReturnMessage.SUCCESS.getCode()))
        {
            LogUtils.warn("通知活动服务器不成功，如果是重启游戏服则需要检查 -> " + result.getString("message"));
            return false;
        }

        return true;
    }

    /**
     * 通知认证服务器自己的状态
     */
    public void notifyServerStatus(SGCommonProto.E_SERVER_STATUS status)
    {
        //通知服务器状态
        WorldInfoFactory worldInfo = WorldInfoFactory.getInstance();

        if(Utils.isNullOrEmpty(worldInfo.getAuthServerHost()))
            return;

        String url = GameServer.getInstance().getAuthServerHost() + HttpFactory.SERVER_CHANGE_STATUS + "?serverId={0}&password={1}&status={2}";
        String password = worldInfo.getServerId() + "" + TimeUtils.getCurrentTime(TimeUtils.YYYYMMDD);
        if(status == null)
            status = worldInfo.getServerStatus();
        url = MessageFormat.format(url, worldInfo.getServerId()+"", MD5Utils.encodeUppercase(password), status.getNumber()+"");

        LogUtils.warn("通知服务器更新状态 -> url:" + url + ", status -> " + status);
        HttpJsonUtils.httpPost(url, null);
    }


    public String getAuthServerHost()
    {
        String result = WorldInfoFactory.getInstance().getAuthServerHost();
        if(StringUtils.isNullOrEmpty(result))
            return "";

        return result.endsWith("\\/") ? result : result + "/";
    }


    /**
     *
     *
     * @param playerController
     */
    public void playerOffline(PlayerController playerController, boolean sendOffline, E_OffLineType offLineType)
    {
        LogUtils.warn(offLineType + " -> " + playerController.getObjectIndex() + " -> " + playerController.getName() + " -> " + (playerController.getTcpConnection()!=null?"从主服离线":"之前已经从战斗服离线"));

        playerController.getPlayer().setLastLoginTime(new Date());
        if(sendOffline)
        {
            ServerManager.getInstance().sendOffline(playerController);

            playerController.sendOffline();
        }

        if(offLineType != E_OffLineType.OFFLINE_OVERTIME)
        {
            savePlayerSync(playerController.getPlayer());
            worldManager.playerExitGame(playerController, offLineType);
            logoutLog(playerController.getObjectIndex(), playerController.getIp(), offLineType.name());
        }
        else
        {
            worldManager.playerExitGame(playerController, offLineType);

            savePlayer(playerController.getPlayer());

            startThread(() -> {
                logoutLog(playerController.getObjectIndex(), playerController.getIp(), offLineType.name());
            });
        }



    }


    public void savePlayer(Player player)
    {
        startThread(() -> {
            PlayerService playerService = SpringContext.getBean(PlayerService.class);
            playerService.update(player);
        });
    }

    public void savePlayerSync(Player player)
    {
        PlayerService playerService = SpringContext.getBean(PlayerService.class);
        playerService.update(player);
    }

    public void savePlayerList(List<Player> playerList, boolean updateBase, Class... clsList)
    {
        if(playerList.isEmpty())
            return;

        startThread(() -> {
            PlayerService playerService = SpringContext.getBean(PlayerService.class);
            playerService.updatePlayers(playerList, updateBase, clsList);
        });
    }


    public void savePlayerListSync(List<Player> playerList, boolean updateBase, Class... clsList)
    {
        if(playerList.isEmpty())
            return;

        PlayerService playerService = SpringContext.getBean(PlayerService.class);
        playerService.updatePlayers(playerList, updateBase, clsList);
    }

    public void savePlayerSync(Player player, boolean updateBase, Class... clsList)
    {
        PlayerService playerService = SpringContext.getBean(PlayerService.class);
        playerService.updatePlayer(player, updateBase, clsList);
    }


    public void saveIllegality(IllegalityModel model)
    {
        if(model == null)
            return;

        startThread(() -> {
            IllegalityService illegalityMapper = SpringContext.getBean(IllegalityService.class);
            illegalityMapper.create(model);
        });
    }

    /**
     * 每天0点定时更新在线的玩家充值记录
     */
    public void updateRechargeCount(){
        PlayerService playerService = SpringContext.getBean(PlayerService.class);
        List<PlayerController> playerList = GameServer.getInstance().getWorldManager().getPlayerList();
        TwoTuple<Long, Long> inDayTime = CoreUtils.getTimeActivity();
        for(PlayerController controller : playerList){
            playerService.queryTotalRechargeCount(controller.getPlayer(), inDayTime.getFirst(), inDayTime.getSecond());
        }
    }

    /**
     * 获取新的玩家流水号
     * @return
     */
    public String createPlayerIndex()
    {
        return WorldInfoFactory.getInstance().createPlayerIndex();
    }


    public void loginLog(SGCommonProto.E_CHANNEL_TYPE channelType, String ip, String playerIndex, String loginType)
    {
        LoginLogModel loginLogModel = new LoginLogModel();
        loginLogModel.setChannel(channelType.getNumber());
        loginLogModel.setIp(ip);
        loginLogModel.setPlayerIndex(playerIndex);
        loginLogModel.setLoginType(loginType);
        loginLogModel.setCreateTime(TimeUtils.getCurrentDate());

        PlayerService playerService = SpringContext.getBean(PlayerService.class);
        playerService.loginLog(loginLogModel);
    }

    public void logoutLog(String playerIndex, String ip, String offlineType)
    {
        LogoutLogModel logoutLogModel = new LogoutLogModel();
        logoutLogModel.setPlayerIndex(playerIndex);
        logoutLogModel.setIp(ip);
        logoutLogModel.setLogoutType(offlineType);
        logoutLogModel.setCreateTime(new Date());

        PlayerService playerService = SpringContext.getBean(PlayerService.class);
        playerService.logoutLog(logoutLogModel);
    }



    public boolean getPlatformConfigFromAuth(String platform)
    {
        String url = GameServer.getInstance().getAuthServerHost() + HttpFactory.PLATFORM_GET_CONFIG + "?serverId=" + GameServer.getInstance().getServerId() + "&password=" + MD5Utils.encodeUppercase(GameServer.getInstance().getServerId() + ConstantFactory.RECHARGE_KEY + TimeUtils.getCurrentTime(TimeUtils.YYYYMMDD)) + "&platform={0}";
        JSONObject payConfigJson = HttpJsonUtils.httpPost(MessageFormat.format(url, platform));
        if(payConfigJson == null || !payConfigJson.containsKey("code") || !payConfigJson.getString("code").equals(ReturnMessage.SUCCESS.getCode()))
        {
            LogUtils.error("从认证服获取冲值配置错误,请检查-> " + url + " result -> " + payConfigJson);
            return false;
        }

        JSONObject dataJson = payConfigJson.getJSONObject("data");
        if(dataJson.containsKey(PlatformFactory.KEY_TENPAY))
        {
            TenpayConfig.init(dataJson.getJSONObject(PlatformFactory.KEY_TENPAY));
        }
        if(dataJson.containsKey(PlatformFactory.KEY_ALIPAY))
        {
            AlipayConfig.init(dataJson.getJSONObject(PlatformFactory.KEY_ALIPAY));
        }
        if(dataJson.containsKey(PlatformFactory.KEY_OTHER))
        {
            PlatformFactory.init(dataJson.getJSONObject(PlatformFactory.KEY_OTHER));
        }
        if(!Utils.isNullOrEmpty(platform) && !dataJson.containsKey(platform))
            return false;

        return true;
    }

    public void updateActivity(){
        synchronized (activityWeekInfoList){
            activityWeekInfoList = CoreUtils.initActivityWeekInfoList();
            weekEndTime = CoreUtils.getInWeekTime().getSecond();
        }

        synchronized (activityDayInfoList){
            activityDayInfoList = CoreUtils.initActivityDayHallInfoList();
            dayEndTime = CoreUtils.getInDayTime().getSecond();
        }
    }

    /**
     * 周历对所有玩家展示的都一样 一周更新一次
     * @return
     */
    public List<SGCommonProto.WeekCalendarColumn.Builder> getActivityWeekInfoList() {
        if (weekEndTime < DataFactory.currentTime){
            synchronized (activityWeekInfoList){
                if (activityWeekInfoList.isEmpty()){
                    activityWeekInfoList = CoreUtils.initActivityWeekInfoList();
                    weekEndTime = CoreUtils.getInWeekTime().getSecond();
                }
            }
        }
        return activityWeekInfoList;
    }

    /**
     * 日历列表同一天对所有玩家都一样 具体的状态根据当前时间确定 具体的次数根据玩家确定
     * @param player
     * @return
     */
    public List<SGCommonProto.ActivityHallColumn.Builder> getActivityDayInfoList(final Player player) {
        if (dayEndTime < DataFactory.currentTime){
            synchronized (activityDayInfoList){
                if (activityDayInfoList.isEmpty()){
                    activityDayInfoList = CoreUtils.initActivityDayHallInfoList();
                    dayEndTime = CoreUtils.getInDayTime().getSecond();
                }
            }
        }
        return  CoreUtils.getActivityDayHallInfoByPlayer(player,activityDayInfoList);
    }

    public WorldArenaData getWorldArenaData()
    {
        return WorldInfoFactory.getInstance().getWorldArenaData();
    }

    public WorldOfficialData getWorldOfficialData()
    {
        return WorldInfoFactory.getInstance().getWorldOfficialData();
    }

    public WorldBossData getWorldBossData()
    {
        return WorldInfoFactory.getInstance().getWorldBossData();
    }

    public WorldAuctionData getWorldAuctionData(){ return WorldInfoFactory.getInstance().getWorldAuctionData(); }

//    public WorldMapData getWorldMapData()
//    {
//        return WorldInfoFactory.getInstance().getWorldMapData("");
//    }

    public void removePlayerWorldInfo(Player player)
    {
        WorldInfoFactory.getInstance().removePlayer(player);

        worldManager.removeRankPlayerDetail(player);
    }


    public boolean isRunning() {
        return isRunning;
    }

    public boolean isOpen() {
        if(SpringContext.isDev())
            return true;
        return isOpen;
    }

    public void setOpen(boolean isOpen) {
        this.isOpen = isOpen;
    }

    public ExecutorService getService() {
        return service;
    }

    public MainFrame getMainFrame() {
        return mainFrame;
    }

    public void setMainFrame(MainFrame mainFrame) {
        this.mainFrame = mainFrame;
    }

    public Map<Integer, WorldAuctionBean> getAuctionMap(){
        return WorldInfoFactory.getInstance().getWorldAuctionData().getAuctionMap();
    }

    public String getServerHost() {
        return serverHost;
    }

    public void setServerHost(String serverHost) {
        this.serverHost = serverHost;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

}
